package com.kexilo.core.common.validation;

import com.kexilo.core.common.annotation.IdCard;
import com.kexilo.core.common.utils.StringUtils;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;

/**
 * 身份证验证器
 * 
 * @author Kexilo
 */
public class IdCardValidator implements ConstraintValidator<IdCard, String> {
    
    /**
     * 身份证号正则表达式
     * 支持15位和18位身份证号
     */
    private static final String ID_CARD_PATTERN = 
        "^[1-9]\\d{5}(18|19|([23]\\d))\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]$";
    
    /**
     * 15位身份证号正则表达式
     */
    private static final String ID_CARD_15_PATTERN = 
        "^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}$";
    
    @Override
    public void initialize(IdCard constraintAnnotation) {
        // 初始化方法，可以获取注解的属性
    }
    
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        // 空值由@NotNull或@NotBlank来验证
        if (StringUtils.isEmpty(value)) {
            return true;
        }
        
        // 验证18位身份证号
        if (value.length() == 18) {
            return value.matches(ID_CARD_PATTERN) && validateChecksum(value);
        }
        
        // 验证15位身份证号
        if (value.length() == 15) {
            return value.matches(ID_CARD_15_PATTERN);
        }
        
        return false;
    }
    
    /**
     * 验证18位身份证号校验位
     * 
     * @param idCard 身份证号
     * @return 是否有效
     */
    private boolean validateChecksum(String idCard) {
        if (idCard.length() != 18) {
            return false;
        }
        
        try {
            // 加权因子
            int[] weights = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
            // 校验码
            char[] checksums = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};
            
            int sum = 0;
            for (int i = 0; i < 17; i++) {
                sum += Character.getNumericValue(idCard.charAt(i)) * weights[i];
            }
            
            int mod = sum % 11;
            char checksum = checksums[mod];
            
            return checksum == Character.toUpperCase(idCard.charAt(17));
        } catch (NumberFormatException e) {
            return false;
        }
    }
}
